//
//  UITableView+HACDispatcher.m
//  HACDispatcher
//
//  Created by Chengjie on 2019/5/9.
//

#import "UITableView+HACDispatcher.h"
#import <objc/runtime.h>
#define HAC_EPSILON 1e-6

@interface HACTableViewDispatcher ()
@property (nonatomic, strong) NSMutableArray<id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>> *allImplementor;
@end

@implementation HACTableViewDispatcher

- (instancetype)initWithTableView:(UITableView *)tableView {
    self = [super init];
    if (self) {
        _allImplementor = [NSMutableArray array];
        _tableView = tableView;
        _openWeightRanking = NO;
    }
    return self;
}

- (NSArray<id<UITableViewDataSource,UITableViewDelegate>> *)implementorList {
    if (!_allImplementor || !_allImplementor.count) {
        return nil;
    }
    return [_allImplementor copy];
}

- (NSUInteger)indexOfImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)implementor {
    NSUInteger index = [_allImplementor indexOfObject:implementor];
    if (index < _allImplementor.count) {
        return index;
    }
    return NSUIntegerMax;
}

- (id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)implementorAtIndex:(NSUInteger)index {
    if (index < _allImplementor.count) {
        return [_allImplementor objectAtIndex:index];
    }
    return nil;
}

- (void)reloadTableView {
    if (self.openWeightRanking) {
        NSArray *sortArr = [_allImplementor sortedArrayUsingComparator:^NSComparisonResult(id<HACTableViewSectionProtocol>  _Nonnull obj1, id<HACTableViewSectionProtocol>  _Nonnull obj2) {
            CGFloat deta = obj1.rankingWeight - obj2.rankingWeight;
            if (fabs(deta) < HAC_EPSILON) {
                return NSOrderedSame;
            } else if (deta > 0) {
                return NSOrderedAscending;
            } else {
                return NSOrderedDescending;
            }
        }];
        [_allImplementor removeAllObjects];
        [_allImplementor addObjectsFromArray:sortArr];
    }
    
    [_allImplementor enumerateObjectsUsingBlock:^(id<UITableViewDataSource,UITableViewDelegate,HACTableViewSectionProtocol>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj setTableView:self.tableView];
        [obj setViewController:self.viewController];
        [obj registTableReusableView];
    }];
    [self.tableView reloadData];
}

- (void)reoloadDataWithImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)implementor {
    NSUInteger index = [_allImplementor indexOfObject:implementor];
    if (index >= _allImplementor.count) {
        return;
    }
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:UITableViewRowAnimationFade];
}

- (void)reoloadDataWithImplementorArr:(NSArray<id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>> *)implementorArr {
    [implementorArr enumerateObjectsUsingBlock:^(id<UITableViewDataSource,UITableViewDelegate,HACTableViewSectionProtocol>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [self reoloadDataWithImplementor:obj];
    }];
}

- (void)updateViewController:(UIViewController *)viewController {
    _viewController = viewController;
    [_allImplementor enumerateObjectsUsingBlock:^(id<UITableViewDataSource,UITableViewDelegate,HACTableViewSectionProtocol>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj setViewController:viewController];
    }];
}

#pragma mark -ScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewDidScroll:scrollView];
    }
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewDidZoom:scrollView];
    }
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewWillBeginDragging:scrollView];
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
    }
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewWillBeginDecelerating:scrollView];
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewDidEndDecelerating:scrollView];
    }
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewDidEndScrollingAnimation:scrollView];
    }
}

- (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        return [_scrollDelegate viewForZoomingInScrollView:scrollView];
    }
    return nil;
}

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view  {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewWillBeginZooming:scrollView withView:view];
    }
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewDidEndZooming:scrollView withView:view atScale:scale];
    }
}

- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        return [_scrollDelegate scrollViewShouldScrollToTop:scrollView];
    }
    return YES;
}

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView{
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        [_scrollDelegate scrollViewDidScrollToTop:scrollView];
    }
}

- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView {
    if (_scrollDelegate && [_scrollDelegate respondsToSelector:_cmd]) {
        if (@available(iOS 11.0, *)) {
            [_scrollDelegate scrollViewDidChangeAdjustedContentInset:scrollView];
        } else {
            // Fallback on earlier versions
        }
    }
}

#pragma mark -CRUD

- (void)replaceImplementorList:(NSArray<id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>> *)implementorArr {
    if (_allImplementor.count) {
        [_allImplementor removeAllObjects];
    }
    [_allImplementor addObjectsFromArray:implementorArr ?: @[]];
    [self reloadTableView];
}

- (void)replaceFromRange:(NSRange)range useImplementorArr:(NSArray<id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>> *)implementorArr {
    if (!_allImplementor.count) {
        [self replaceImplementorList:implementorArr];
    }
    NSRange maxRange = NSMakeRange(0, _allImplementor.count);
    if (!range.length || range.location > maxRange.length || (range.length + range.location) > maxRange.length) {
        return;
    }
    [_allImplementor replaceObjectsInRange:range withObjectsFromArray:implementorArr];
    [self reloadTableView];
}

- (void)replaceImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)implementor atIndex:(NSUInteger)index {
    if (index >= _allImplementor.count) {
        return;
    }
    [_allImplementor replaceObjectAtIndex:index withObject:implementor];
    [self reloadTableView];
}

- (void)replaceImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)implementor useAnImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)anImplementor {
    NSUInteger replaceIndex = [_allImplementor indexOfObject:implementor];
    [self replaceImplementor:anImplementor atIndex:replaceIndex];
}

- (void)exchangeImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)implementor otherImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)otherImplementor {
    NSUInteger index_0 = [_allImplementor indexOfObject:implementor];
    NSUInteger Index_1 = [_allImplementor indexOfObject:otherImplementor];
    [self exchangeImplementorAtIndex:index_0 otherImplementorIndex:Index_1];
}

- (void)exchangeImplementorAtIndex:(NSUInteger)index otherImplementorIndex:(NSUInteger)otherIndex {
    if (index >= _allImplementor.count || otherIndex >= _allImplementor.count || index == otherIndex) {
        return;
    }
    if (self.openWeightRanking) {
        id<HACTableViewSectionProtocol> anImplementor = [_allImplementor objectAtIndex:index];
        id<HACTableViewSectionProtocol> otherImplementor = [_allImplementor objectAtIndex:otherIndex];
        CGFloat otherRankingWeight = otherImplementor.rankingWeight;
        otherImplementor.rankingWeight = anImplementor.rankingWeight;
        anImplementor.rankingWeight = otherRankingWeight;
    }
    [_allImplementor exchangeObjectAtIndex:index withObjectAtIndex:otherIndex];
    [self reloadTableView];
}

- (void)clearImplementorList {
    [_allImplementor removeAllObjects];
    [self reloadTableView];
}

- (void)appendingImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)implementor {
    [_allImplementor addObject:implementor];
    [self reloadTableView];
}

- (void)appendingImplementorArr:(NSArray<id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>> *)implementorArr {
    if (!implementorArr.count) {
        return;
    }
    [_allImplementor addObjectsFromArray:implementorArr];
    [self reloadTableView];
}

- (void)deleteImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)implementor {
    [_allImplementor removeObject:implementor];
    [self reloadTableView];
}

- (void)deleteImplementorArr:(NSArray<id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>> *)implementorArr {
    if (!implementorArr.count) {
        return;
    }
    [_allImplementor removeObjectsInArray:implementorArr];
    [self reloadTableView];
}

- (void)deleteImplementorAtIndex:(NSUInteger)index {
    if (index >= _allImplementor.count) {
        return;
    }
    [_allImplementor removeObjectsAtIndexes:[NSIndexSet indexSetWithIndex:index]];
    [self reloadTableView];
}

- (void)deleteImplementorAtIndexSet:(NSIndexSet *)indexSet {
    [_allImplementor removeObjectsAtIndexes:indexSet];
    [self reloadTableView];
}

- (void)insertImplementor:(id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>)implementor atIndex:(NSUInteger)index {
    if (index > _allImplementor.count) {
        return;
    }
    [_allImplementor insertObject:implementor atIndex:index];
    [self reloadTableView];
}

- (void)insertImplementorArr:(NSArray<id<UITableViewDataSource, UITableViewDelegate, HACTableViewSectionProtocol>> *)implementorArr atIndexSet:(nonnull NSIndexSet *)indexSet {
    [_allImplementor insertObjects:implementorArr atIndexes:indexSet];
    [self reloadTableView];
}

#pragma mark - 基础展示和事件转发

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.implementorList.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [[self.implementorList objectAtIndex:section] tableView:tableView numberOfRowsInSection:section];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    return [[self.implementorList objectAtIndex:indexPath.section] tableView:tableView cellForRowAtIndexPath:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView heightForRowAtIndexPath:indexPath];
    } else {
        return 0;
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    id implementor = [self.implementorList objectAtIndex:section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView heightForHeaderInSection:section];
    } else {
        return 0;
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    id implementor = [self.implementorList objectAtIndex:section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView heightForFooterInSection:section];
    } else {
        return 0;
    }
}

- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    id implementor = [self.implementorList objectAtIndex:section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView viewForHeaderInSection:section];
    } else {
        return nil;
    }
}

- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    id implementor = [self.implementorList objectAtIndex:section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView viewForFooterInSection:section];
    } else {
        return nil;
    }
}

#pragma mark - 选中和取消选中

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView didSelectRowAtIndexPath:indexPath];
    } else {
        return;
    }
}

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView didDeselectRowAtIndexPath:indexPath];
    } else {
        return;
    }
}

#pragma mark - 元素曝光

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath];
    } else {
        return;
    }
}

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
    id implementor = [self.implementorList objectAtIndex:section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView willDisplayHeaderView:view forSection:section];
    } else {
        return;
    }
}

- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section {
    id implementor = [self.implementorList objectAtIndex:section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView willDisplayFooterView:view forSection:section];
    } else {
        return;
    }
}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView didEndDisplayingCell:cell forRowAtIndexPath:indexPath];
    } else {
        return;
    }
}

- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section  {
    id implementor = [self.implementorList objectAtIndex:section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView didEndDisplayingHeaderView:view forSection:section];
    } else {
        return;
    }
}

- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section {
    id implementor = [self.implementorList objectAtIndex:section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView didEndDisplayingFooterView:view forSection:section];
    } else {
        return;
    }
}

#pragma mark - 高亮

- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath  {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView shouldHighlightRowAtIndexPath:indexPath];
    } else {
        return YES;
    }
}

- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView didHighlightRowAtIndexPath:indexPath];
    } else {
        return;
    }

}

- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath  {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView didUnhighlightRowAtIndexPath:indexPath];
    } else {
        return;
    }

}

#pragma mark - 编辑

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView canEditRowAtIndexPath:indexPath];
    } else {
        return NO;
    }
}

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView editingStyleForRowAtIndexPath:indexPath];
    } else {
        return UITableViewCellEditingStyleNone;
    }
}

- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView titleForDeleteConfirmationButtonForRowAtIndexPath:indexPath];
    } else {
        return nil;
    }
}

- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath  API_AVAILABLE(ios(11.0)){
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView trailingSwipeActionsConfigurationForRowAtIndexPath:indexPath];
    } else {
        return nil;
    }
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    id implementor = [self.implementorList objectAtIndex:indexPath.section];
    if ([implementor respondsToSelector:_cmd]) {
        return [implementor tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath];
    } else {
        return;
    }
}

@end

@implementation UITableView (HACDispatcher)

- (BOOL)hac_forceUseDispatcher {
    NSNumber *number = objc_getAssociatedObject(self, _cmd);
    if (number) {
        return number.boolValue;
    }
    self.hac_forceUseDispatcher = NO;
    return NO;
}

- (void)setHac_forceUseDispatcher:(BOOL)hac_forceUseDispatcher {
    objc_setAssociatedObject(self, @selector(hac_forceUseDispatcher), @(hac_forceUseDispatcher), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    if (hac_forceUseDispatcher) {
        self.dataSource = self.hac_dispatcher;
        self.delegate = self.hac_dispatcher;
    }
}

- (HACTableViewDispatcher *)hac_dispatcher {
    HACTableViewDispatcher *dispatcher = objc_getAssociatedObject(self, _cmd);
    if (!dispatcher) {
        dispatcher = [[HACTableViewDispatcher alloc] initWithTableView:self];
        objc_setAssociatedObject(self, _cmd, dispatcher, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return dispatcher;
}

- (void)hac_setBelongToViewController:(UIViewController *)viewController {
    [self.hac_dispatcher updateViewController:viewController];
}

@end
